﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using ConfigurationSettings;
using CustomControls;
using System;
using FileSystemControls;
using FileSystemControls.ListViewItems;
using FileSystemControls.Support;
using FileTracker.Forms;

//TODO:Find @" and get a list of strings
//TODO:Reduce the list of strings and create a Settings file from them
using ServiceLocator;

namespace FileTracker
{
    public class MainController : ControllerBase
    {
        private IApplicationSettings _localSettings;

        public event EventHandler<EventArgs> Exit;
        private delegate void KeyMethod();

        readonly Dictionary<Keys, KeyMethod> _localKeyMapping;

        public MainModel Model
        {
            get
            {
                return (MainModel)_model;
            }
        }

        public MainController()
        {
            _localKeyMapping = new Dictionary<Keys, KeyMethod>();
            _localSettings = Locator.Resolve<IApplicationSettings>();
        }

        protected override void InitializeModel()
        {
            _model = new MainModel();
        }

        protected override void InitializeView()
        {
            _view = new MainView();
            ((MainView)_view).Controller = this;
        }

        protected override void ShowView()
        {
            _view.Show();
        }

        public void OnExit()
        {
            Exit.Raise(this, new EventArgs());
        }

        /// <summary>
        /// Scans the configuration object and adds handlers for any key event that is configured.
        /// </summary>
        private void LoadKeys()
        {
            try
            {
                IApplicationSettings localSettings = Locator.Resolve<IApplicationSettings>();

                _localKeyMapping.Clear();

                //Loop through keys
                foreach (KeyValuePair<KeyEnum, Keys> currentPair in localSettings.KeyList)
                {
                    if (currentPair.Value != Keys.None)
                    {
                        //Switch based on enum
                        switch (currentPair.Key)
                        {
                            //set key to correct method call
                            case KeyEnum.COPY:
                                _localKeyMapping.Add(currentPair.Value, Copy);
                                break;
                            case KeyEnum.DELETE:
                                _localKeyMapping.Add(currentPair.Value, DeleteItems);
                                break;
                            case KeyEnum.MAKEDIR:
                                _localKeyMapping.Add(currentPair.Value, MakeDir);
                                break;
                            case KeyEnum.MOVE:
                                _localKeyMapping.Add(currentPair.Value, MoveItem);
                                break;
                            case KeyEnum.REFRESH:
                                _localKeyMapping.Add(currentPair.Value, RefreshPath);
                                break;
                            case KeyEnum.RENAME:
                                _localKeyMapping.Add(currentPair.Value, RenameItems);
                                break;
                            case KeyEnum.TOUCH:
                                _localKeyMapping.Add(currentPair.Value, TouchItems);
                                break;
                        }
                    }
                }
            }
            catch (Exception loadKeysEx)
            {
                MessageBox.Show(string.Format("Error in LoadKeys{0}", loadKeysEx.Message));
            }
        }

        public void AddFavorite()
        {
            try
            {
                //Code to add favorites
                FormAddFavorite addFavoriteForm = new FormAddFavorite { CurrentPath = Model.SourceListView.CurrentPath };

                addFavoriteForm.ShowDialog(_view);

                LoadFavorites();
            }

            catch (Exception addFavoriteEx)
            {
                MessageBox.Show(string.Format("Error in toolStripMenuItemAddFavorite_Click {0}", addFavoriteEx.Message));
            }
        }

        public void EditFavorite()
        {
            //Code to edit favorites

            try
            {
                //Code to add favorites
                FormEditFavorites editFavoriteForm = new FormEditFavorites();
                editFavoriteForm.ShowDialog(_view);

                LoadFavorites();
            }
            catch (Exception editFavoriteEx)
            {
                MessageBox.Show(string.Format("Error in toolStripMenuItemEditFavorites_Click{0}", editFavoriteEx.Message));
            }
        }

        public void LoadFavorites()
        {
            Model.SourceListView.LoadFavorites();
            Model.DestListView.LoadFavorites();
        }

        public void SetPreferences()
        {
            IApplicationSettings localSettings = new ApplicationSettings();

            FormPreferences formPreferences = new FormPreferences
            {
                Location = _localSettings.PreferenceWindowLocation,
                Size = _localSettings.PreferenceWindowSize,
                SplitterDistance = _localSettings.PreferencesSplitterDistance
            };

            formPreferences.ShowDialog(_view);

            formPreferences.Dispose();

            SetConfiguration();
        }

        public void RefreshPath()
        {
            Model.SourceListView.RefreshPath();
        }

        public void Copy()
        {
            if (Model.SourceListView.SelectedItems.Count <= 0)
            {
                MessageBox.Show(@"No items selected.");
                return;
            }

            if (Model.SourceListView.CurrentPath == Model.DestListView.CurrentPath)
            {
                MessageBox.Show(@"Source and Destination Directories can't be the same. Try Duplicate instead.", @"Copying Items");
                return;
            }

            try
            {
                CopyItems(Model.SourceListView.SelectedItems, Model.DestListView.CurrentPath);

                Model.SourceListView.RefreshPath();
                Model.DestListView.RefreshPath();
            }
            catch (IOException)
            {
                MessageBox.Show(@"Unable to copy file.  Check permissions, destination file is not in use, or drive is not full.");
            }
            catch (Exception innerCopyEx)
            {
                //Messagebox telling the user that we encountered an error and may not have finished copying the file.
                MessageBox.Show(string.Format("Unexpected Exception: {0}\r\nFile was not copied correctly.", innerCopyEx.Message));
            }
        }

        //This call can contain files or directories
        private void CopyItems(List<FileSystemListViewItem> argSelectedItems, string argDestPath)
        {
            FormCopyProgress copyForm = new FormCopyProgress(argSelectedItems, argDestPath);

            copyForm.ShowDialog(_view);
        }

        public void MoveItem()
        {
            FormYesNoYestoall messageForm = new FormYesNoYestoall();
            bool forceAction = !_localSettings.AskBeforeDelete; //If I do "ask" I don't "force"

            if (Model.SourceListView.CurrentPath == Model.DestListView.CurrentPath)
            {
                MessageBox.Show(@"Source and Destination can't be the same.", @"Moving Items");
                return;
            }

            if (Model.SourceListView.SelectedItems.Count <= 0)
            {
                MessageBox.Show(@"No items selected.");
                return;
            }

            //025 - Implement FileWatchers to keep views up-to-date //This idea is being discounted in favor of an out-of-process service with a message pump.
            //fileSystemListViewLeft.SuspendWatching();
            //fileSystemListViewRight.SuspendWatching();

            //Move FileList or folders
            foreach (FileListViewItem currentListViewItem in Model.SourceListView.SelectedFileItems)
            {
                if (currentListViewItem.IsOffline)
                {
                    MessageBox.Show(string.Format("File {0} is offline. You cannot move it.", currentListViewItem.Name), @"Move File");
                    return;
                }

                DialogResultEx moveFileResult;
                if (currentListViewItem.IsReadOnly && !forceAction)
                {
                    messageForm.ShowDialog(string.Format("File {0} is read-only.  Do you still want to move it?", currentListViewItem.Name), @"Move File");
                    moveFileResult = messageForm.DialogResultEx;

                    if (moveFileResult == DialogResultEx.No)
                    {
                        return;
                    }

                    if (moveFileResult == DialogResultEx.YesToAll)
                    {
                        forceAction = true;
                    }
                }

                if (currentListViewItem.IsSystem && !forceAction)
                {
                    messageForm.ShowDialog("File " + currentListViewItem.Name + " is a system file. Are you sure you still want to move it?", "Move File");
                    moveFileResult = messageForm.DialogResultEx;

                    if (moveFileResult == DialogResultEx.No)
                    {
                        return;
                    }

                    if (moveFileResult == DialogResultEx.YesToAll)
                    {
                        forceAction = true;
                    }
                }

                FileInfo destFile = new FileInfo(Model.DestListView.CurrentPath + "\\" + currentListViewItem.Name);
                //If exists check read-only
                if (destFile.Exists && !forceAction)
                {
                    messageForm.ShowDialog("Destination file already exists with that name (" + destFile.Name + ").  Do you want to overwrite it?", "Move File");
                    moveFileResult = messageForm.DialogResultEx;

                    if (moveFileResult == DialogResultEx.No)
                    {
                        return;
                    }

                    if (moveFileResult == DialogResultEx.YesToAll)
                    {
                        forceAction = true;
                    }

                    if (destFile.IsReadOnly && !forceAction)
                    {
                        messageForm.ShowDialog("Destination file " + destFile.Name + " is read-only.  Do you want to overwrite it?", "Move File");
                        moveFileResult = messageForm.DialogResultEx;

                        if (moveFileResult == DialogResultEx.No)
                        {
                            return;
                        }

                        if (moveFileResult == DialogResultEx.YesToAll)
                        {
                            forceAction = true;
                        }
                    }
                }

                currentListViewItem.MoveTo(Model.DestListView.CurrentPath, forceAction);

            }

            foreach (DirectoryListViewItem currentListViewItem in Model.SourceListView.SelectedDirectoryItems)
            {
                //If it's a directory then move it (FileManager doesn't check for ReadOnly subFiles so I won't either)
                currentListViewItem.MoveTo(Model.DestListView.CurrentPath);
            }

            //fileSystemListViewLeft.ResumeWatching();
            //fileSystemListViewRight.ResumeWatching();

            Model.SourceListView.RefreshPath();
            Model.DestListView.RefreshPath();
        }

        public void MakeDir()
        {
            FormMakeDir makeDir = new FormMakeDir(Model.SourceListView.CurrentPath);
            //_sourceListView.SuspendWatching();

            makeDir.ShowDialog(_view);

            //_sourceListView.ResumeWatching();

        }

        public void DeleteItems()
        {
            FormYesNoYestoall messageForm = new FormYesNoYestoall();
            bool yesToAll = !_localSettings.AskBeforeDelete;
            bool forceDelete = false;

            if (Model.SourceListView.SelectedItems.Count <= 0)
            {
                MessageBox.Show(@"No items selected.");
                return;
            }


            //_sourceListView.SuspendWatching();
            //Code to delete FileList or folders
            foreach (FileListViewItem currentListViewItem in Model.SourceListView.SelectedFileItems)
            {
                if (!yesToAll)
                {
                    messageForm.ShowDialog("Are you sure you want to delete " + currentListViewItem.Name, "Delete Confirmation");
                    DialogResultEx verifyDeleteResult = messageForm.DialogResultEx;
                    if (verifyDeleteResult == DialogResultEx.No)
                    {
                        continue;
                    }

                    if (verifyDeleteResult == DialogResultEx.Yes)
                    {
                        forceDelete = true;
                    }

                    if (verifyDeleteResult == DialogResultEx.YesToAll)
                    {
                        yesToAll = true;
                        forceDelete = true;
                    }

                    if (currentListViewItem.IsSystem && !yesToAll)
                    {
                        messageForm.ShowDialog(currentListViewItem.Name + " is a system file.  Are you sure you want to delete it?", "Confirm Delete");
                        verifyDeleteResult = messageForm.DialogResultEx;
                        if (verifyDeleteResult == DialogResultEx.No)
                        {
                            continue;
                        }

                        if (verifyDeleteResult == DialogResultEx.Yes)
                        {
                            forceDelete = true;
                        }

                        if (verifyDeleteResult == DialogResultEx.YesToAll)
                        {
                            yesToAll = true;
                            forceDelete = true;
                        }
                    }

                    if (currentListViewItem.IsReadOnly && !yesToAll)
                    {
                        messageForm.ShowDialog(currentListViewItem.Name + " is a read-only file.  Are you sure you want to delete it?", "Confirm Delete");
                        verifyDeleteResult = messageForm.DialogResultEx;
                        if (verifyDeleteResult == DialogResultEx.No)
                        {
                            continue;
                        }

                        if (verifyDeleteResult == DialogResultEx.Yes)
                        {
                            forceDelete = true;
                        }

                        if (verifyDeleteResult == DialogResultEx.YesToAll)
                        {
                            yesToAll = true;
                            forceDelete = true;
                        }
                    }

                    if (currentListViewItem.IsOffline && !yesToAll)
                    {
                        MessageBox.Show(string.Format("{0} is currently offline.  Cannot delete at this time.", currentListViewItem.Name), @"Cannot Delete File");
                    }
                } //end if confirm delete

                currentListViewItem.Delete(forceDelete);
            }

            foreach (DirectoryListViewItem currentListViewItem in Model.SourceListView.SelectedDirectoryItems)
            {
                if (Model.DestListView.CurrentPath == currentListViewItem.Path)
                {
                    Model.DestListView.SetPath(currentListViewItem.ParentDirectory);
                }

                try
                {
                    if (_localSettings.AskBeforeDelete)
                    {
                        DialogResult directoryDelete = MessageBox.Show(string.Format("Are you sure you want to delete {0} and all it's subdirectories?", currentListViewItem.Name), @"Confirm Delete", MessageBoxButtons.YesNo);

                        if (directoryDelete == DialogResult.No)
                        {
                            continue;
                        }

                        currentListViewItem.Delete(!_localSettings.AskBeforeDelete);
                    }
                }
                catch (FileIsReadOnlyException readonlyException)
                {
                    FileInfo readonlyFile = readonlyException.ReadonlyFile;
                    messageForm.ShowDialog("File " + readonlyFile.Name + " is ReadOnly. Are you sure you want to delete it?", "Delete ReadOnly File");
                    DialogResultEx deleteResult = messageForm.DialogResultEx;
                    if (deleteResult == DialogResultEx.Yes)
                    {
                        readonlyFile.IsReadOnly = false;
                        readonlyFile.Delete();
                        DeleteItems();
                    }
                    if (deleteResult == DialogResultEx.YesToAll)
                    {
                        currentListViewItem.Delete(true);
                    }
                }
            } //End Foreach
            //_sourceListView.ResumeWatching();

            Model.SourceListView.RefreshPath();
        }

        public void RenameItems()
        {
            Model.SourceListView.RenameItems();
        }

        public void TouchItems()
        {
            if (Model.SourceListView.SelectedFileItems.Count() <= 0)
            {
                MessageBox.Show(@"No FileList selected.");
                return;
            }

            try
            {
                //Loop through the selected FileList
                foreach (FileListViewItem currentItem in Model.SourceListView.SelectedFileItems)
                {
                    currentItem.Touch();
                }

                Model.SourceListView.RefreshPath();
            }
            catch (Exception touchEx)
            {
                MessageBox.Show(string.Format("Error in toolStripButtonTouch_Click{0}", touchEx.Message));
            }

        }

        public void EnableFilter(bool argEnableFilter)
        {
            Model.SourceListView.EnableFilter = argEnableFilter;
        }

        public void ModifyDate()
        {
            FormModifyDate modifyForm = new FormModifyDate(Model.SourceListView.SelectedFileItems.ToList());
            modifyForm.ShowDialog(_view);
            Model.SourceListView.RefreshPath();
        }

        public void SetConfiguration()
        {
            Model.SourceListView.SetConfiguration();
            Model.DestListView.SetConfiguration();

            //LoadDefaultDirectories
            LoadDefaultDirectories();

            //LoadKeys
            LoadKeys();
        }

        public void AddOpenWith()
        {
            try
            {
                using (FormAddOpenWith addOpenWithForm = new FormAddOpenWith())
                {
                    addOpenWithForm.ShowDialog(_view);
                }

                Model.SourceListView.LoadOpenWith();
                Model.DestListView.LoadOpenWith();

            }
            catch (Exception addOpenWithEx)
            {
                MessageBox.Show(string.Format("Error in toolStripMenuItemAddOpenWith_Click {0}", addOpenWithEx.Message));
            }
        }

        public void EditOpenWith()
        {
            try
            {
                //Code to add favorites
                FormEditOpenWith editOpenWithForm = new FormEditOpenWith();
                editOpenWithForm.ShowDialog(_view);

                Model.SourceListView.LoadOpenWith();
                Model.DestListView.LoadOpenWith();
            }
            catch (Exception editOpenWithEx)
            {
                MessageBox.Show(string.Format("Error in toolStripMenuItemEditOpenWith_Click{0}", editOpenWithEx.Message));
            }
        }

        public void SynchPathEvent(object argSender, EventArgs argEvtArgs)
        {
            //If this is called from the source view set the dest to the source
            if (argSender == Model.SourceListView)
            {
                Model.DestListView.SetPath(Model.SourceListView.CurrentPath);
            }
            else
            {
                Model.SourceListView.SetPath(Model.DestListView.CurrentPath);
            }
        }

        public void SyncDirs()
        {
            //Synch left dir into the right
            //Get a list of FileList in LeftDir
            //DirectoryInfo leftDirInfo = new DirectoryInfo(Model.SourceListView.CurrentPath);

            //Get a list of FileList in RightDir
            //DirectoryInfo rightDirInfo = new DirectoryInfo(Model.DestListView.CurrentPath);

            try
            {
                //Ask if this is really what the user intends

                DialogResult syncResult = MessageBox.Show(_view,
                    string.Format("This operation will synchronise the contents of both directories ('{0}' and '{1}'). This may take a considerable amount of time" +
                                  " and may destroy data (if it is duplicated in both places). Are you sure you want to do this?",
                        Model.SourceListView.CurrentPath, Model.DestListView.CurrentPath),
                    @"Verify Sync Operation", MessageBoxButtons.YesNo, MessageBoxIcon.Warning,
                    MessageBoxDefaultButton.Button2);

                if (syncResult == DialogResult.No)
                {
                    return;
                }

                var leftDirItems = Model.SourceListView.Files.Cast<FileSystemListViewItem>().ToList();

                CopyItems(leftDirItems, Model.DestListView.CurrentPath);

                #region *** Left Copy ***

                ////Loop through FileList in LeftDir
                //foreach (FileInfo leftFile in leftFileList)
                //{
                //    //  Find item in RightDir
                //    FileInfo tempRightFile = new FileInfo(argRightDirInfo.FullName + "\\" + leftFile.Name);

                //    //  if found, compare
                //    if (tempRightFile.Exists)
                //    {
                //        //  if found, compare dates
                //        if (tempRightFile.LastWriteTime < leftFile.LastWriteTime)
                //        {
                //            //      if RightDir.Date < LeftDir.Date, copy
                //            tempRightFile.IsReadOnly = false;
                //            leftFile.CopyTo(tempRightFile.FullName, true);
                //        }
                //    }
                //    else //if not found, copy
                //    {
                //        //TempRightFile.FullName
                //        leftFile.CopyTo(tempRightFile.FullName);
                //    }
                //}

                #endregion

                var rightDirItems = Model.DestListView.Files.Cast<FileSystemListViewItem>().ToList();

                CopyItems(rightDirItems, Model.SourceListView.CurrentPath);

                #region *** Right Copy ***

                ////Loop through FileList in RightDir
                //foreach (FileInfo rightFile in rightFileList)
                //{
                //    //  Find item in LeftDir
                //    FileInfo tempLeftFile = new FileInfo(leftDirInfo.FullName + "\\" + rightFile.Name);

                //    //  if found, compare
                //    if (tempLeftFile.Exists)
                //    {
                //        //  if found, compare dates
                //        if (tempLeftFile.LastWriteTime < rightFile.LastWriteTime)
                //        {
                //            //      if LeftDir.Date < RightDir.Date, copy
                //            tempLeftFile.IsReadOnly = false;
                //            rightFile.CopyTo(tempLeftFile.FullName, true);
                //        }

                //    }
                //    else //if not found, copy
                //    {
                //        rightFile.CopyTo(tempLeftFile.FullName);
                //    }
                //}

                #endregion

                leftDirItems = Model.SourceListView.Directories.Cast<FileSystemListViewItem>().ToList();
                CopyItems(leftDirItems, Model.DestListView.CurrentPath);

                #region *** Left Dir Copy ***

                ////Now sync directories.
                //DirectoryInfo[] leftDirList = leftDirInfo.GetDirectories();

                //foreach (DirectoryInfo leftDir in leftDirList)
                //{
                //    DirectoryInfo rightTempDir = new DirectoryInfo(rightDirInfo.FullName + "\\" + leftDir.Name);
                //    if (!rightTempDir.Exists)
                //    {
                //        rightTempDir = Directory.CreateDirectory(rightDirInfo.FullName + "\\" + leftDir.Name);
                //    }

                //    SyncDirs(leftDir, rightTempDir);
                //}

                #endregion

                rightDirItems = Model.DestListView.Directories.Cast<FileSystemListViewItem>().ToList();
                CopyItems(rightDirItems, Model.SourceListView.CurrentPath);

                #region *** Right Dir Copy ***

                //DirectoryInfo[] rightDirList = rightDirInfo.GetDirectories();
                //foreach (DirectoryInfo rightDir in rightDirList)
                //{
                //    DirectoryInfo leftTempDir = new DirectoryInfo(leftDirInfo.FullName + "\\" + rightDir.Name);
                //    if (!leftTempDir.Exists)
                //    {
                //        leftTempDir = Directory.CreateDirectory(leftDirInfo.FullName + "\\" + rightDir.Name);
                //    }

                //    SyncDirs(rightDir, leftTempDir);
                //}

                #endregion
            }
            catch (Exception syncDirsEx)
            {
                MessageBox.Show(string.Format("Error in toolStripButtonSynchDir_Click: {0}", syncDirsEx.Message));
            }

            Model.SourceListView.RefreshPath();
            Model.DestListView.RefreshPath();
        }

        /// <summary>
        /// Initializes the FileSystemListViews to their default directories.
        /// </summary>
        private void LoadDefaultDirectories()
        {
            try
            {
                //Load Left pane default directories
                string leftDefaultFolder;
                if (_localSettings.LeftDefaultFolderType == FolderType.FavoriteFolder)
                {
                    leftDefaultFolder = _localSettings.FavoriteList.ContainsKey(_localSettings.LeftDefaultFolder) ?
                        _localSettings.FavoriteList[_localSettings.LeftDefaultFolder] :
                        new DriveInfo(Application.ExecutablePath).RootDirectory.FullName;
                }
                else
                {
                    leftDefaultFolder = _localSettings.LeftDefaultFolder;
                }

                Model.SourceListView.SetPath(leftDefaultFolder);

                //Load Right pane default directories
                string rightDefaultFolder;
                if (_localSettings.RightDefaultFolderType == FolderType.FavoriteFolder)
                {
                    rightDefaultFolder = _localSettings.FavoriteList.ContainsKey(_localSettings.RightDefaultFolder) ?
                        _localSettings.FavoriteList[_localSettings.RightDefaultFolder] :
                        new DriveInfo(Application.ExecutablePath).RootDirectory.FullName;
                }
                else
                {
                    rightDefaultFolder = _localSettings.RightDefaultFolder;
                }

                Model.DestListView.SetPath(rightDefaultFolder);
            }

            catch (Exception loadDefaultEx)
            {
                MessageBox.Show(string.Format("Error in LoadDefaultDirectories{0}", loadDefaultEx.Message));
            }
        }

        public bool KeyDown(Keys argEventKey)
        {
            if (_localKeyMapping.ContainsKey(argEventKey))
            {
                KeyMethod callingMethod = _localKeyMapping[argEventKey];
                callingMethod.Invoke();
                return true;
            }

            return false;
        }

    }
}
